package util.concurrent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;

/**
 * FutureTask实现的熔断工具类
 * Created by yzy on 2020-01-09 11:52
 */
public class FutureUtil {
    private static final Logger logger = LoggerFactory.getLogger(FutureUtil.class);
    private static final long DEFAULT_TIME_OUT_SECONDS = 5L;

    private static final ThreadPoolExecutor FUTURE_TASK_EXECUTOR = new ThreadPoolExecutor(
            5,          //核心线程数
            30,     //最大线程数
            10,        //线程存活时间
            TimeUnit.SECONDS,        //时间单位
            new LinkedBlockingQueue<>(100), //队列大小为100
            new ThreadPoolExecutor.CallerRunsPolicy()        //重试添加当前任务，会重复调用execute();
    );

    /**
     * 使用默认的超时时间，获取异步任务执行的结果
     * @param call
     * @param <T>
     * @return
     */
    public static <T> T getByTimeout(Callable<T> call){
        return getByTimeout(call, DEFAULT_TIME_OUT_SECONDS, TimeUnit.SECONDS);
    }

    /**
     * 在指定超时时间内，获取异步任务执行的结果
     * 如果超时，则结束任务并返回null
     * @param call
     * @param timeout
     * @param timeUnit
     * @param <T>
     * @return
     */
    public static <T> T getByTimeout(Callable<T> call, long timeout, TimeUnit timeUnit){
        Future<T> task = FUTURE_TASK_EXECUTOR.submit(call);
        try {
            long taskStart = System.currentTimeMillis();
            T res = task.get(timeout, timeUnit);
            long taskEnd = System.currentTimeMillis();
            logger.info("FutureTask get success by {}ms, timeout={} {}", (taskEnd-taskStart), timeout, timeUnit);
            return res;
        } catch (TimeoutException e) {
            boolean cancel = task.cancel(true);
            logger.info("FutureTask is timeout in {} {}, cancelTask={}", timeout, timeUnit, cancel, e);
        } catch (Exception e) {
            boolean cancel = task.cancel(true);
            logger.info("FutureTask submit Exception, cancelTask={}", cancel, e);
        }
        return null;
    }
}